package javapop.framework.parser;

import java.util.ArrayList;
import java.util.List;

import javapop.framework.ParseContext;
import javapop.framework.ParseError;
import javapop.framework.ParseInput;
import javapop.framework.ParseResult;
import javapop.framework.Parser;

public class Many<T> extends Parser<List<T>> {
	protected Parser<T> child;
	private int minCount;
	private int maxCount;
	
	public Many(Parser<T> child, int minCount, int maxCount) {
		this.child = child;
		this.minCount = minCount;
		this.maxCount = maxCount;
	}
	
	public Many(Parser<T> child) {
		this(child,0,Integer.MAX_VALUE-1);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public ParseResult<List<T>> eval(ParseContext ctx, ParseInput input) {
		List<T> results = new ArrayList<T>();
		int count = 0;
		while(true) {
			int savePos = input.getPos();
			ParseResult<T> result = child.parse(ctx,input);
			if(result.isError() || savePos==input.getPos()) {
				if(result.isError() && result.asError().isFatal()) {
					return (ParseResult) result;
				}
				if(count<minCount) {
					return (ParseResult) new ParseError(result.asError(),"Expecting at least "+minCount+" "+child.getDescription());
				}
				
				return buildResult(results);
				
			} else if(!result.isNull()) {
				results.add(result.getResult());
			}
			count++;
			if(count>maxCount) {
				return (ParseResult) new ParseError("Expecting at most "+maxCount+" "+child.getDescription());
			}
		}
	}
			
	@Override
	public String getDescription() {
		StringBuffer buf = new StringBuffer();
		if(minCount==0 && maxCount==Integer.MAX_VALUE-1) {
			buf.append("(");
			buf.append(child.getDescription());
			buf.append(")*");
		} else {
			buf.append("Many[");
			buf.append(minCount);
			buf.append("..");
			buf.append(maxCount);
			buf.append("](");
			buf.append(child.getDescription());
			buf.append(")");
		}
		return buf.toString();
	}
	
	@Override
	public boolean isMemoized() {
		return false;
	}
}
